<template>
  <!-- <transition name="viewer-fade"> -->
  <div
    tabindex="-1"
    ref="wrapperRef"
    class="el-image-viewer__wrapper"
    :style="{ 'z-index': viewerZIndex }"
  >
    <div class="el-image-viewer__mask" @click.self="handleMaskClick"></div>
    <!-- CLOSE -->
    <span class="el-image-viewer__btn el-image-viewer__close" @click="hide">
      <i class="el-icon-close"></i>
    </span>
    <!-- ARROW -->
    <template v-if="!isSingle">
      <span
        class="el-image-viewer__btn el-image-viewer__prev"
        :class="{ 'is-disabled': !infinite && isFirst }"
        @click="prev"
      >
        <i class="el-icon-arrow-left" />
      </span>
      <span
        class="el-image-viewer__btn el-image-viewer__next"
        :class="{ 'is-disabled': !infinite && isLast }"
        @click="next"
      >
        <i class="el-icon-arrow-right" />
      </span>
    </template>
    <!-- ACTIONS -->
    <div class="el-image-viewer__btn el-image-viewer__actions">
      <div class="el-image-viewer__actions__inner">
        <i class="el-icon-zoom-out" @click="handleActions('zoomOut')"></i>
        <i class="el-icon-zoom-in" @click="handleActions('zoomIn')"></i>
        <i class="el-image-viewer__actions__divider"></i>
        <i :class="mode.icon" @click="toggleMode"></i>
        <i class="el-image-viewer__actions__divider"></i>
        <i class="el-icon-refresh-left" @click="handleActions('anticlocelise')"></i>
        <i class="el-icon-refresh-right" @click="handleActions('clocelise')"></i>
        <i class="el-icon-download img-btn-icon" @click="handleActions('download')"></i>
      </div>
    </div>
    <!-- CANVAS -->
    <div class="el-image-viewer__canvas">
      <template v-for="(url, i) in urlList">
        <img
          v-if="i === index"
          ref="imgRefs"
          class="el-image-viewer__img"
          :key="url"
          :src="currentImg"
          :style="imgStyle"
          @load="handleImgLoad"
          @error="handleImgError"
          @mousedown="handleMouseDown"
        />
      </template>
    </div>
  </div>
  <!-- </transition> -->
</template>

<script setup lang="ts">
import { template } from 'lodash'
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
import type { Ref } from 'vue'

// 定义模式常量
const Mode = {
  CONTAIN: {
    name: 'contain',
    icon: 'el-icon-full-screen',
  },
  ORIGINAL: {
    name: 'original',
    icon: 'el-icon-c-scale-to-original',
  },
} as const

type ModeType = (typeof Mode)[keyof typeof Mode]

// 定义 Props
interface Props {
  urlList?: string[]
  zIndex?: number
  onSwitch?: (index: number) => void
  onClose?: () => void
  initialIndex?: number
  appendToBody?: boolean
  maskClosable?: boolean
  fileName?: string
}

const props = withDefaults(defineProps<Props>(), {
  urlList: () => [],
  zIndex: 2000,
  onSwitch: () => {},
  onClose: () => {},
  initialIndex: 0,
  appendToBody: true,
  maskClosable: true,
  fileName: '',
})

// 定义 Emits
const emit = defineEmits<{
  (e: 'close'): void
}>()

// 定义引用
const wrapperRef = ref<HTMLElement | null>(null)
const imgRefs = ref<HTMLImageElement[]>([])
const index = ref(props.initialIndex)
const infinite = ref(true)
const loading = ref(false)
const mode = ref<ModeType>(Mode.CONTAIN)
const transform = ref({
  scale: 1,
  deg: 0,
  offsetX: 0,
  offsetY: 0,
  enableTransition: false,
})

// 定义事件处理器引用
let keyDownHandler: ((e: KeyboardEvent) => void) | null = null
let mouseWheelHandler: ((e: WheelEvent) => void) | null = null
let dragHandler: ((e: MouseEvent) => void) | null = null

// 计算属性
const isSingle = computed(() => props.urlList.length <= 1)
const isFirst = computed(() => index.value === 0)
const isLast = computed(() => index.value === props.urlList.length - 1)
const currentImg = computed(() => props.urlList[index.value])
const imgStyle = computed(() => {
  const { scale, deg, offsetX, offsetY, enableTransition } = transform.value
  const style: Record<string, string> = {
    transform: `scale(${scale}) rotate(${deg}deg)`,
    transition: enableTransition ? 'transform .3s' : '',
    'margin-left': `${offsetX}px`,
    'margin-top': `${offsetY}px`,
  }
  if (mode.value === Mode.CONTAIN) {
    style.maxWidth = style.maxHeight = '100%'
  }
  return style
})
const viewerZIndex = computed(() => {
  // 简化 zIndex 计算逻辑，因为在 Vue 3 项目中可能没有 PopupManager
  return props.zIndex
})

// 监听器
watch(index, (val) => {
  reset()
  props.onSwitch(val)
})

watch(currentImg, () => {
  nextTick(() => {
    if (imgRefs.value && imgRefs.value[0]) {
      const img = imgRefs.value[0]
      if (!img.complete) {
        loading.value = true
      }
    }
  })
})

// 方法定义
const hide = () => {
  deviceSupportUninstall()
  props.onClose()
  emit('close')
}

const rafThrottle = <T extends (...args: any[]) => any>(fn: T): T => {
  let locked = false
  return function (this: any, ...args: any[]) {
    if (locked) return
    locked = true
    window.requestAnimationFrame(() => {
      fn.apply(this, args)
      locked = false
    })
  } as T
}

const deviceSupportInstall = () => {
  keyDownHandler = (e: KeyboardEvent) => {
    e.stopPropagation()
    const keyCode = e.key
    switch (keyCode) {
      // ESC
      case 'Escape':
        hide()
        break
      // SPACE
      case ' ':
        toggleMode()
        break
      // LEFT_ARROW
      case 'ArrowLeft':
        prev()
        break
      // UP_ARROW
      case 'ArrowUp':
        handleActions('zoomIn')
        break
      // RIGHT_ARROW
      case 'ArrowRight':
        next()
        break
      // DOWN_ARROW
      case 'ArrowDown':
        handleActions('zoomOut')
        break
    }
  }

  mouseWheelHandler = rafThrottle((e: WheelEvent) => {
    const delta = e.deltaY ? -e.deltaY : (e as any).wheelDelta
    if (delta > 0) {
      handleActions('zoomIn', {
        zoomRate: 0.015,
        enableTransition: false,
      })
    } else {
      handleActions('zoomOut', {
        zoomRate: 0.015,
        enableTransition: false,
      })
    }
  })

  document.addEventListener('keydown', keyDownHandler)
  document.addEventListener('wheel', mouseWheelHandler)
}

const deviceSupportUninstall = () => {
  if (keyDownHandler) {
    document.removeEventListener('keydown', keyDownHandler)
    keyDownHandler = null
  }
  if (mouseWheelHandler) {
    document.removeEventListener('wheel', mouseWheelHandler)
    mouseWheelHandler = null
  }
}

const handleImgLoad = () => {
  loading.value = false
}

const handleImgError = (e: Event) => {
  loading.value = false
  const target = e.target as HTMLImageElement
  target.alt = '加载失败'
}

const handleMouseDown = (e: MouseEvent) => {
  if (loading.value || e.button !== 0) return

  const { offsetX, offsetY } = transform.value
  const startX = e.pageX
  const startY = e.pageY

  dragHandler = rafThrottle((ev: MouseEvent) => {
    transform.value.offsetX = offsetX + ev.pageX - startX
    transform.value.offsetY = offsetY + ev.pageY - startY
  })

  document.addEventListener('mousemove', dragHandler)
  document.addEventListener('mouseup', () => {
    if (dragHandler) {
      document.removeEventListener('mousemove', dragHandler)
      dragHandler = null
    }
  })

  e.preventDefault()
}

const handleMaskClick = () => {
  if (props.maskClosable) {
    hide()
  }
}

const reset = () => {
  transform.value = {
    scale: 1,
    deg: 0,
    offsetX: 0,
    offsetY: 0,
    enableTransition: false,
  }
}

const toggleMode = () => {
  if (loading.value) return

  const modeKeys = Object.keys(Mode) as (keyof typeof Mode)[]
  const modeValues = Object.values(Mode)
  const currentIndex = modeValues.indexOf(mode.value)
  const nextIndex = (currentIndex + 1) % modeKeys.length
  mode.value = Mode[modeKeys[nextIndex] as keyof typeof Mode]
  reset()
}

const prev = () => {
  if (isFirst.value && !infinite.value) return
  const len = props.urlList.length
  index.value = (index.value - 1 + len) % len
}

const next = () => {
  if (isLast.value && !infinite.value) return
  const len = props.urlList.length
  index.value = (index.value + 1) % len
}

interface ActionOptions {
  zoomRate?: number
  rotateDeg?: number
  enableTransition?: boolean
}

const handleActions = (action: string, options: ActionOptions = {}) => {
  if (loading.value) return
  const { zoomRate = 0.2, rotateDeg = 90, enableTransition = true } = options
  const transformValue = transform.value

  switch (action) {
    case 'zoomOut':
      if (transformValue.scale > 0.2) {
        transformValue.scale = parseFloat((transformValue.scale - zoomRate).toFixed(3))
      }
      break
    case 'zoomIn':
      transformValue.scale = parseFloat((transformValue.scale + zoomRate).toFixed(3))
      break
    case 'clocelise':
      transformValue.deg += rotateDeg
      break
    case 'anticlocelise':
      transformValue.deg -= rotateDeg
      break
    case 'download':
      props.urlList.forEach((item) => {
        const imageUrl = item
        const xhr = new XMLHttpRequest()
        xhr.open('GET', imageUrl, true)
        xhr.responseType = 'blob'
        const imgSubName = props.fileName
        xhr.onload = () => {
          if (xhr.status === 200) {
            const url = window.URL.createObjectURL(xhr.response)
            const link = document.createElement('a')
            link.href = url
            link.download = imgSubName
            link.click()
          } else {
            // 使用 Element Plus 的消息提示替代 this.$message
            console.error('此照片不支持下载')
          }
        }
        xhr.send()
      })
      break
  }
  transformValue.enableTransition = enableTransition
}

// 生命周期钩子
onMounted(() => {
  deviceSupportInstall()
  if (props.appendToBody && wrapperRef.value) {
    document.body.appendChild(wrapperRef.value)
  }
  // add tabindex then wrapper can be focusable via Javascript
  // focus wrapper so arrow key can't cause inner scroll behavior underneath
  if (wrapperRef.value) {
    wrapperRef.value.focus()
  }
})

onBeforeUnmount(() => {
  // if appendToBody is true, remove DOM node after destroy
  if (props.appendToBody && wrapperRef.value && wrapperRef.value.parentNode) {
    wrapperRef.value.parentNode.removeChild(wrapperRef.value)
  }
  deviceSupportUninstall()
})
</script>
